package sudoku.model;

import java.net.URI;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;

import utils.Constants;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;

/**
 * Esta clase tiene que tener todo el manejo de tablero, todos los metodos que
 * se usan en modelo que tienen que ver con tablero van en esta clase
 * 
 */
public class Board
{

	// tablero con que se va a jugar
	private int[][]	board_;

	private int[][]	solvedBoard_;

	private int[][]	auxiliarBoard_	= new int[9][9];

	// cantidad de movimientos del usuario.
	private int			nMovementAmount_;

	// pistas que se generan.
	private int			nClue_;
	private int			nRowClue_;
	private int			nColumnClue_;

	/**
	 * @param difficulty
	 *          {@link Integer}
	 *	Utiliza el web service para pedir un tablero al servidor
	 */
	public Board(int difficulty) {
		ClientConfig config = new DefaultClientConfig();
		Client client = Client.create(config);
		WebResource service = client.resource(getBaseURI());
		String generarString;
		switch (difficulty) {
		case Constants.FACIL:
			generarString = service.path("rest").path("sudokuFacil")
					.accept(MediaType.TEXT_PLAIN).get(String.class);
			break;
		case Constants.MEDIANA:
			generarString = service.path("rest").path("sudokuMedio")
					.accept(MediaType.TEXT_PLAIN).get(String.class);
			break;

		default:// DIFICIL
			generarString = service.path("rest").path("sudokuDificil")
					.accept(MediaType.TEXT_PLAIN).get(String.class);
			break;
		}
		board_ = stringToBoard(generarString, 0);
		solvedBoard_ = stringToBoard(generarString, 81);
		
		// copia tablero
		for (int i = 0; i < board_.length; i++) {
			for (int j = 0; j < board_.length; j++) {
				auxiliarBoard_[i][j] = board_[i][j];
			}
		}
		nMovementAmount_ = 0;
	}

	/**
	 * @param string
	 * @param desde
	 * @return la matriz cargada con valores que se encuentra expresada como un string
	 */
	private int[][] stringToBoard(String string, int desde)
	{
		int[][] matriz = new int[9][9];

		for (int i = 0; i < matriz.length; ++i) {
			for (int j = 0; j < matriz.length; ++j) {
				matriz[i][j] = Integer.parseInt(("" + string.charAt(desde)));
				++desde;
			}// end for j
		}// end for i
		return matriz;
	}// end cargarStringMatrizO

	private URI getBaseURI()
	{
		return // UriBuilder.fromUri("http://s156.eatj.com/servlet/myupload").build();
		UriBuilder.fromUri("http://192.168.1.15:8080/service.sudoku").build(); // localhost:8080
	}

	/**
	 * @return cantidad de movimientos hechos sobre el tablero
	 */
	public int getMovementAmount()
	{
		return nMovementAmount_;
	}

	/**
	 * @param row
	 * @param column
	 * @return numero en una posicion dada del tablero
	 */
	public int getBoardPos(int row, int column)
	{
		return this.board_[row][column];
	}

	/**
	 * @param row
	 * @param column
	 * @param number
	 *          Cambia un numero en el tablero y aumenta la cantidad de
	 *          movimientos. Este metodo es llamado cuando el usuario realiza un
	 *          cambio
	 */
	public void setBoardPos(int row, int column, int number)
	{
		this.board_[row][column] = number;
		++this.nMovementAmount_;
	}

	/**
	 * @return true ssi el sudoku ha sido resuelto correctamente
	 */
	public boolean isSolvedSudoku()
	{
		boolean isOk = true;
		for (int i = 0; i < board_.length && isOk; ++i) {
			for (int j = 0; j < board_.length && isOk; ++j) {
				isOk = board_[i][j] == solvedBoard_[i][j];
			}
		}
		return isOk;
	}

	/**
	 * @return {@link Integer[][]} Tablero actual de juego
	 */
	public int[][] getBoard()
	{
		return board_;
	}

	/**
	 * @return {@link Integer[][]} Tablero inicial de juego
	 */
	public int[][] getInitialBoard()
	{
		return auxiliarBoard_;
	}

	/**
	 * Restaura el tablero al que se inicio con nuevo juego
	 */
	public void restoreBoard()
	{
		// clono el tablero.
		for (int i = 0; i < 9; i++) {
			for (int j = 0; j < 9; j++) {
				this.board_[i][j] = this.auxiliarBoard_[i][j];
			}
		}
		nMovementAmount_ *= 2;
	}

	/**
	 * Resuelve el sudoku que se esta usando por el usuario
	 */
	public void solveBoard()
	{
		board_ = solvedBoard_;
	}

	/**
	 * @return true ssi se genero una pista. Este metodo tambien, si la genera, la
	 *         coloca en el sudoku.
	 */
	public boolean canGenerateClue()
	{
		boolean generatedClue = false;
		int col = 0;
		int fil = 0;
		while ((!generatedClue) && (col < board_.length)) {
			fil = 0;
			while ((!generatedClue) && (fil < board_.length)) {
				boolean areEqual = (board_[fil][col] == solvedBoard_[fil][col]);
				if ((board_[fil][col] == 0) || !areEqual) {
					board_[fil][col] = solvedBoard_[fil][col];
					generatedClue = true;
				}
				if (!generatedClue)
					++fil;
			} // end while
			if (!generatedClue)
				++col;
		} // end while
		if (generatedClue) {
			// carga los datos de la pista
			nClue_ = board_[fil][col];
			nRowClue_ = fil;
			nColumnClue_ = col;			
		}
		return generatedClue;
	}

	/**
	 * @return el valor que se pondrá en el tablero como pista
	 */
	public int getGeneratedClue()
	{
		return nClue_;
	}

	/**
	 * @return la fila del tablero en la que la pista debe ser colocada
	 */
	public int getRowClue()
	{
		return nRowClue_;
	}

	/**
	 * @return la columna del tablero en la que la pista debe ser colocada
	 */
	public int getColClue()
	{
		return nColumnClue_;
	}

}
